package cn.stimd.spring.beans.factory.support;

import cn.stimd.spring.beans.MutablePropertyValues;
import cn.stimd.spring.beans.factory.config.BeanDefinition;
import org.springframework.core.AttributeAccessorSupport;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;

/**
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public abstract class AbstractBeanDefinition extends AttributeAccessorSupport implements BeanDefinition {
    private volatile Object beanClass;              //可能是Class或String类型
    private String scope = "";
    private int role = BeanDefinition.ROLE_APPLICATION;
    private boolean primary;
    private boolean lazyInit = false;
    private boolean synthetic = false;
    private MutablePropertyValues propertyValues;   //存放通过属性访问进行设置的值
    private String factoryBeanName;                 //工厂方法所在的类的名称
    private String factoryMethodName;               //工厂方法名称
    private String initMethodName;                  //初始化方法名
    private String destroyMethodName;               //销毁方法名

    protected AbstractBeanDefinition(){}

    protected AbstractBeanDefinition(BeanDefinition original) {
        setBeanClassName(original.getBeanClassName());
        setLazyInit(original.isLazyInit());
        setFactoryBeanName(original.getFactoryBeanName());
        setFactoryMethodName(original.getFactoryMethodName());
        setPropertyValues(new MutablePropertyValues(original.getPropertyValues()));
        setRole(original.getRole());

        if (original instanceof AbstractBeanDefinition) {
            AbstractBeanDefinition originalAbd = (AbstractBeanDefinition) original;
            if(originalAbd.hasBeanClass()){
                setBeanClass(originalAbd.getBeanClass());
            }

            setInitMethodName(originalAbd.getInitMethodName());
            setDestroyMethodName(originalAbd.getDestroyMethodName());
        }
    }

    public Class<?> getBeanClass() {
        return (Class<?>) beanClass;
    }

    public void setBeanClass(Object beanClass) {
        this.beanClass = beanClass;
    }

    public boolean isSingleton(){
        return "singleton".equals(scope) || "".equals(scope);
    }

    @Override
    public String getBeanClassName() {
        Object beanClassObject = this.beanClass;
        if (beanClassObject instanceof Class) {
            return ((Class<?>) beanClassObject).getName();
        }
        else {
            return (String) beanClassObject;
        }
    }

    @Override
    public void setBeanClassName(String beanClassName) {
        this.beanClass = beanClassName;
    }

    public boolean isSynthetic() {
        return synthetic;
    }

    public void setSynthetic(boolean synthetic) {
        this.synthetic = synthetic;
    }

    @Override
    public int getRole() {
        return role;
    }

    public void setRole(int role) {
        this.role = role;
    }

    @Override
    public boolean isLazyInit() {
        return lazyInit;
    }

    @Override
    public void setLazyInit(boolean lazyInit) {
        this.lazyInit = lazyInit;
    }

    @Override
    public boolean isPrimary() {
        return primary;
    }

    public void setPrimary(boolean primary) {
        this.primary = primary;
    }

    public void setPropertyValues(MutablePropertyValues propertyValues) {
        this.propertyValues = propertyValues;
    }

    @Override
    public MutablePropertyValues getPropertyValues() {
        if (this.propertyValues == null) {
            this.propertyValues = new MutablePropertyValues();
        }
        return this.propertyValues;
    }

    public String getInitMethodName() {
        return initMethodName;
    }

    public void setInitMethodName(String initMethodName) {
        this.initMethodName = initMethodName;
    }

    public String getDestroyMethodName() {
        return destroyMethodName;
    }

    public void setDestroyMethodName(String destroyMethodName) {
        this.destroyMethodName = destroyMethodName;
    }

    @Override
    public String getFactoryBeanName() {
        return factoryBeanName;
    }

    @Override
    public void setFactoryBeanName(String factoryBeanName) {
        this.factoryBeanName = factoryBeanName;
    }

    @Override
    public String getFactoryMethodName() {
        return factoryMethodName;
    }

    @Override
    public void setFactoryMethodName(String factoryMethodName) {
        this.factoryMethodName = factoryMethodName;
    }


    /**
     * 解析Bean的真实类型
     */
    public Class<?> resolveBeanClass(ClassLoader classLoader) throws ClassNotFoundException{
        String className = getBeanClassName();
        if(className == null){
            return null;
        }

        Class<?> resolvedClass = ClassUtils.forName(className, classLoader);
        this.beanClass = resolvedClass;
        return resolvedClass;
    }


    /**
     * 检查BeanDefinition是否已经解析了Class
     */
    public boolean hasBeanClass() {
        return (this.beanClass instanceof Class);
    }


    @Override
    public int hashCode() {
        int hashCode = ObjectUtils.nullSafeHashCode(getBeanClassName());
        hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.scope);
        hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.propertyValues);
        hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.factoryBeanName);
        hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.factoryMethodName);
        hashCode = 29 * hashCode + super.hashCode();
        return hashCode;
    }
}
